﻿using System;
using System.Configuration;
using System.Threading;
using LaBruteTrainer.Business;
using LaBruteTrainer.Configuration;

namespace LaBruteTrainer
{
    public class Program
    {
        private static Semaphore workerPool;
        private static readonly object workerLocker = new object();
        private static int WorkerCount;

        [STAThread]
        private static void Main(string[] Args)
        {
            if (Args.Length != 1) return;
            string action = GetAction(Args);
            if (string.IsNullOrEmpty(action)) return;


            var bruteConfiguration = (BruteConfiguration) ConfigurationManager.GetSection("MyBrutes");
            if (bruteConfiguration.myBrutes.Count == 0) return;
            int threadLimit = Convert.ToInt32(ConfigurationManager.AppSettings["ThreadLimit"]);
            workerPool = new Semaphore(threadLimit, threadLimit);

            Console.WriteLine("Start processing\n");

            foreach (var brute in bruteConfiguration.myBrutes)
            {
                lock (workerLocker)
                {
                    var thread = actionFactory(action);
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start(brute);
                    ++WorkerCount;
                }
            }

            lock (workerLocker)
            {
                while (WorkerCount > 0)
                    Monitor.Wait(workerLocker);
            }
            Console.WriteLine("Done.");
            Console.ReadLine();
        }

        /// <summary>
        /// Gets the action.
        /// </summary>
        /// <param name="Args">The args.</param>
        /// <returns></returns>
        private static string GetAction(string[] Args) {
            string action = "";
            switch (Args[0])
            {
                case "tournament":
                    action = "tournament";
                    break;
                case "train":
                    action = "train";
                    break;
            }
            return action;
        }

        /// <summary>
        /// Actions the factory.
        /// </summary>
        /// <param name="action">The action.</param>
        /// <returns></returns>
        private static Thread actionFactory(string action)
        {
            return action == "train"? 
                new Thread(BruteTrainerWorker):
                new Thread(BruteInscriptionWorker);
        }

        #region Workers
        
        /// <summary>
        /// Brutes the trainer worker.
        /// </summary>
        /// <param name="brute">The brute.</param>
        private static void BruteTrainerWorker(object brute)
        {
            workerPool.WaitOne();
            using (var bruteBusiness = new BruteBusiness())
            {
                try
                {
                    bool trainingCompleted;
                    do
                    {
                        trainingCompleted = bruteBusiness.AttackBrute(((BruteCfg) brute).Name,
                                                                      ((BruteCfg) brute).Password);
                    } while (!trainingCompleted);

                    lock (workerLocker)
                    {
                        bruteBusiness.ShowCombats();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    lock (workerLocker)
                    {
                        --WorkerCount;
                        workerPool.Release();
                        Monitor.Pulse(workerLocker);
                    }
                }
            }
        }

        /// <summary>
        /// Brutes the trainer worker.
        /// </summary>
        /// <param name="brute">The brute.</param>
        private static void BruteInscriptionWorker(object brute)
        {
            workerPool.WaitOne();
            using (var bruteBusiness = new BruteBusiness())
            {
                try
                {
                    bruteBusiness.RegisterTournament(((BruteCfg) brute).Name,
                                                     ((BruteCfg) brute).Password);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    lock (workerLocker)
                    {
                        --WorkerCount;
                        workerPool.Release();
                        Monitor.Pulse(workerLocker);
                    }
                }
            }
        }

        #endregion
    }
}